package com.imis.base.util;

import com.imis.base.constant.CommonConstant;
import lombok.extern.slf4j.Slf4j;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.sql.Date;
import java.util.*;
import java.util.concurrent.ThreadLocalRandom;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * <p>
 * ConvertUtils<br>
 * 一些需要转换用到的工具 工具类
 * </p>
 *
 * @author XinLau
 * @version 1.0
 * @since 2019年03月26日 11:11
 */
@Slf4j
public class ConvertUtils {

    /**
     * 用于随机选的数字
     */
    public static final String BASE_NUMBER = "0123456789";
    /**
     * 用于随机选的字符
     */
    public static final String BASE_CHAR = "abcdefghijklmnopqrstuvwxyz";
    /**
     * 用于随机选的字符和数字
     */
    public static final String BASE_CHAR_NUMBER = BASE_CHAR + BASE_NUMBER;

    /**
     * 对象空判断
     *
     * @param object - 单个对象
     * @return boolean - 只要有元素为Empty，则返回true
     */
    public static Boolean isEmpty(Object object) {
        return (object == null || CommonConstant.EMPTY.equals(object.toString().trim()) || CommonConstant.NULL.equals(object.toString().trim()));
    }

    /**
     * 一次性判断多个或单个对象为空。
     *
     * @param objects - 最少一个对象
     * @return boolean - 只要有一个元素为Empty，则返回true
     */
    public static Boolean isEmpty(Object... objects) {
        Boolean result = false;
        for (Object object : objects) {
            if (isEmpty(object)) {
                result = true;
                break;
            }
        }
        return result;
    }

    /**
     * 对象非空判断
     *
     * @param object - 单个对象
     * @return
     */
    public static Boolean isNotEmpty(Object object) {
        return !isEmpty(object);
    }

    /**
     * 一次性判断多个或单个对象非空判断
     *
     * @param objects - 最少一个对象
     * @return
     */
    public static Boolean isNotEmpty(Object... objects) {
        return !isEmpty(objects);
    }

    /**
     * 是否含有sql注入，返回true表示含有
     *
     * @param obj
     * @return
     */
    public static boolean containsSqlInjection(Object obj) {
        Pattern pattern = Pattern.compile(CommonConstant.SQL_REGULAR_EXPRESSION);
        Matcher matcher = pattern.matcher(obj.toString());
        return matcher.find();
    }

    public static String decode(String strIn, String sourceCode, String targetCode) {
        return code2code(strIn, sourceCode, targetCode);
    }

    private static String code2code(String strIn, String sourceCode, String targetCode) {
        String strOut = CommonConstant.EMPTY;
        if (strIn == null || CommonConstant.EMPTY.equals(strIn.trim())) {
            return strIn;
        }
        try {
            byte[] b = strIn.getBytes(sourceCode);
            for (int i = 0; i < b.length; i++) {
                log.debug(b[i] + "  ");
            }
            strOut = new String(b, targetCode);
        } catch (Exception e) {
            log.error(e.getMessage());
            return strOut;
        }
        return strOut;
    }

    public static String StrToUTF(String strIn, String sourceCode, String targetCode) {
        String strOut = CommonConstant.EMPTY;
        try {
            strOut = new String(strIn.getBytes(sourceCode), targetCode);
        } catch (UnsupportedEncodingException e) {
            log.error(e.getMessage());
        }
        return strOut;
    }

    /**
     * 实体类对象转Map
     *
     * @param obj - 实体类对象
     * @return Map<String, Object>
     * @author XinLau
     * @creed The only constant is change ! ! !
     * @since 2019/9/7 15:22
     */
    public static Map<String, Object> convertBeanToMap(Object obj) {
        if (isEmpty(obj)) {
            return null;
        }
        Map<String, Object> map = new HashMap<>(16);
        try {
            BeanInfo beanInfo = Introspector.getBeanInfo(obj.getClass());
            PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
            for (PropertyDescriptor property : propertyDescriptors) {
                String key = property.getName();
//                过滤class属性
                if (!"class".equals(key)) {
                    // 得到property对应的getter方法
                    Method getter = property.getReadMethod();
                    Object value = getter.invoke(obj);
                    if (isEmpty(obj)) {
                        map.put(key, CommonConstant.EMPTY);
                    } else {
                        map.put(key, value);
                    }
                }
            }
        } catch (Exception e) {
            return map;
        }
        return map;
    }

    /**
     * 字符串对象转数字对象，自定义默认返回值
     *
     * @param string - 字符串
     * @param defval - 自定义默认返回值
     * @return int
     */
    public static int getInt(String string, int defval) {
        if (isNotEmpty(string)) {
            return (defval);
        }
        try {
            return (getInt(string));
        } catch (NumberFormatException e) {
            return (defval);
        }
    }

    /**
     * 字符串对象转数字对象，默认返回0
     *
     * @param string
     * @return int
     */
    public static int getInt(String string) {
        if (isEmpty(string)) {
            return 0;
        }
        try {
            return (Integer.parseInt(string));
        } catch (NumberFormatException e) {
            return 0;
        }
    }

    /**
     * 字符串对象转数字对象，自定义默认返回值，异常时返回0
     *
     * @param string
     * @return int
     */
    public static int getInt(String string, Integer df) {
        if (isEmpty(string)) {
            return df;
        }
        try {
            return (getInt(string));
        } catch (NumberFormatException e) {
            return 0;
        }
    }

    /**
     * 字符串数组转数字数组
     *
     * @param stringArray
     * @return Integer
     */
    public static Integer[] getInts(String[] stringArray) {
        Integer[] integer = new Integer[stringArray.length];
        if (stringArray == null || stringArray.length <= 0) {
            return null;
        }
        for (int i = 0; i < stringArray.length; i++) {
            integer[i] = Integer.parseInt(stringArray[i]);
        }
        return integer;

    }

    /**
     * 字符串对象转双精度浮点数对象，自定义默认返回值
     *
     * @param string - 字符串
     * @param defval - 默认值
     * @return double
     */
    public static double getDouble(String string, double defval) {
        if (isEmpty(string)) {
            return (defval);
        }
        try {
            return (Double.parseDouble(string));
        } catch (NumberFormatException e) {
            return (defval);
        }
    }

    public static double getDouble(Double doubleObject, double defval) {
        if (isEmpty(doubleObject)) {
            return (defval);
        }
        return doubleObject;
    }

    public static int getInt(Object object, int defval) {
        if (isEmpty(object)) {
            return (defval);
        }
        try {
            return (Integer.parseInt(object.toString()));
        } catch (NumberFormatException e) {
            return (defval);
        }
    }

    public static Integer getInt(Object object) {
        if (isEmpty(object)) {
            return null;
        }
        try {
            return (Integer.parseInt(object.toString()));
        } catch (NumberFormatException e) {
            return null;
        }
    }

    public static int getInt(BigDecimal s, int defval) {
        if (isEmpty(s)) {
            return (defval);
        }
        return s.intValue();
    }

    public static Integer[] getIntegerArry(String[] object) {
        int len = object.length;
        Integer[] result = new Integer[len];
        try {
            for (int i = 0; i < len; i++) {
                result[i] = new Integer(object[i].trim());
            }
            return result;
        } catch (NumberFormatException e) {
            return null;
        }
    }

    public static String getString(String s) {
        return (getString(s, CommonConstant.EMPTY));
    }

    public static String getString(Object object) {
        if (isEmpty(object)) {
            return CommonConstant.EMPTY;
        }
        return (object.toString().trim());
    }

    public static String getString(int i) {
        return (String.valueOf(i));
    }

    public static String getString(float i) {
        return (String.valueOf(i));
    }

    public static String getString(String s, String defval) {
        if (isEmpty(s)) {
            return (defval);
        }
        return (s.trim());
    }

    public static String getString(Object s, String defval) {
        if (isEmpty(s)) {
            return (defval);
        }
        return (s.toString().trim());
    }

    public static Long stringToLong(String str) {
        Long test = 0L;
        try {
            test = Long.valueOf(str);
        } catch (Exception e) {
            log.error(e.getMessage());
        }
        return test;
    }

    /**
     * 获取本机IP
     */
    public static String getIp() {
        String ip = null;
        try {
            InetAddress address = InetAddress.getLocalHost();
            ip = address.getHostAddress();

        } catch (UnknownHostException e) {
            log.error(e.getMessage());
        }
        return ip;
    }

    /**
     * 判断一个类是否为基本数据类型。
     *
     * @param clazz 要判断的类。
     * @return true 表示为基本数据类型。
     */
    private static boolean isBaseDataType(Class clazz) {
        return (clazz.equals(String.class) || clazz.equals(Integer.class) || clazz.equals(Byte.class) || clazz.equals(Long.class) || clazz.equals(Double.class) || clazz.equals(Float.class) || clazz.equals(Character.class) || clazz.equals(Short.class) || clazz.equals(BigDecimal.class) || clazz.equals(BigInteger.class) || clazz.equals(Boolean.class) || clazz.equals(Date.class) || clazz.isPrimitive());
    }

    /**
     * @return 本机IP
     * @throws SocketException
     */
    public static String getRealIp() throws SocketException {
//        本地IP，如果没有配置外网IP则返回它
        String localip = null;
//        外网IP
        String netip = null;

        Enumeration<NetworkInterface> netInterfaces = NetworkInterface.getNetworkInterfaces();
        InetAddress ip;
//        是否找到外网IP
        boolean finded = false;
        while (netInterfaces.hasMoreElements() && !finded) {
            NetworkInterface ni = netInterfaces.nextElement();
            Enumeration<InetAddress> address = ni.getInetAddresses();
            while (address.hasMoreElements()) {
                ip = address.nextElement();
                if (!ip.isSiteLocalAddress() && !ip.isLoopbackAddress() && ip.getHostAddress().indexOf(':') == -1) {
//                    外网IP
                    netip = ip.getHostAddress();
                    finded = true;
                    break;
                } else if (ip.isSiteLocalAddress() && !ip.isLoopbackAddress() && ip.getHostAddress().indexOf(':') == -1) {
//                    内网IP
                    localip = ip.getHostAddress();
                }
            }
        }

        if (isNotEmpty(netip)) {
            return netip;
        } else {
            return localip;
        }
    }

    private static final String REGEX = "\\s*|\t|\r|\n";

    /**
     * java去除字符串中的空格、回车、换行符、制表符
     *
     * @param str
     * @return
     */
    public static String replaceBlank(String str) {
        String dest = CommonConstant.EMPTY;
        if (isNotEmpty(str)) {
            Pattern p = Pattern.compile(REGEX);
            Matcher m = p.matcher(str);
            dest = m.replaceAll(CommonConstant.EMPTY);
        }
        return dest;

    }

    /**
     * 判断元素是否在数组内
     *
     * @param substring
     * @param source
     * @return
     */
    public static boolean isIn(String substring, String[] source) {
        if (source == null || source.length == 0) {
            return false;
        }
        for (String aSource : source) {
            if (aSource.equals(substring)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 获取Map对象
     */
    public static Map<Object, Object> getHashMap() {
        return new HashMap<>(16);
    }

    /**
     * SET转换MAP
     *
     * @param setobj
     * @return
     */
    public static Map<Object, Object> SetToMap(Set<Object> setobj) {
        Map<Object, Object> map = getHashMap();
        for (Object o : setobj) {
            Map.Entry<Object, Object> entry = (Map.Entry<Object, Object>) o;
            map.put(entry.getKey().toString(), entry.getValue() == null ? CommonConstant.EMPTY : entry.getValue().toString().trim());
        }
        return map;

    }

    /**
     * 判断私有IP（内网）
     *
     * @param ipAddress - IP地址
     * @return boolean -
     * @author XinLau
     * @creed The only constant is change ! ! !
     * @since 2020/5/11 13:41
     */
    public static boolean isInnerIp(String ipAddress) {
        boolean isInnerIp;
        long ipNum = getIpNum(ipAddress);
        /*
         * 私有IP：  A类 10.0.0.0-10.255.255.255
         *          B类 172.16.0.0-172.31.255.255
         *          C类 192.168.0.0-192.168.255.255
         *          当然，还有127这个网段是环回地址
         **/
        long aBegin = getIpNum("10.0.0.0");
        long aEnd = getIpNum("10.255.255.255");
        long bBegin = getIpNum("172.16.0.0");
        long bEnd = getIpNum("172.31.255.255");
        long cBegin = getIpNum("192.168.0.0");
        long cEnd = getIpNum("192.168.255.255");
        isInnerIp = isInner(ipNum, aBegin, aEnd) || isInner(ipNum, bBegin, bEnd) || isInner(ipNum, cBegin, cEnd) || "127.0.0.1".equals(ipAddress);
        return isInnerIp;
    }

    /**
     * 将IP地址变成数字
     *
     * @param ipAddress - IP地址
     * @return long -
     * @author XinLau
     * @creed The only constant is change ! ! !
     * @since 2020/5/11 13:43
     */
    private static long getIpNum(String ipAddress) {
        String[] ip = ipAddress.split("\\.");
        long a = Long.parseLong(ip[0]);
        long b = Long.parseLong(ip[1]);
        long c = Long.parseLong(ip[2]);
        long d = Long.parseLong(ip[3]);

        return a * 256 * 256 * 256 + b * 256 * 256 + c * 256 + d;
    }

    private static boolean isInner(long userIp, long begin, long end) {
        return (userIp >= begin) && (userIp <= end);
    }

    /**
     * 将下划线大写方式命名的字符串转换为驼峰式。
     * 如果转换前的下划线大写方式命名的字符串为空，则返回空字符串。</br>
     * 例如：hello_world->helloWorld
     *
     * @param name 转换前的下划线大写方式命名的字符串
     * @return 转换后的驼峰式命名的字符串
     */
    public static String camelName(String name) {
        StringBuilder result = new StringBuilder();
        // 快速检查
        if (name == null || name.isEmpty()) {
            // 没必要转换
            return CommonConstant.EMPTY;
        } else if (!name.contains(CommonConstant.UNDERSCORE)) {
            // 不含下划线，仅将首字母小写
            return name.substring(0, 1).toLowerCase() + name.substring(1).toLowerCase();
        }
        // 用下划线将原始字符串分割
        String[] camels = name.split(CommonConstant.UNDERSCORE);
        for (String camel : camels) {
            // 跳过原始字符串中开头、结尾的下换线或双重下划线
            if (camel.isEmpty()) {
                continue;
            }
            // 处理真正的驼峰片段
            if (result.length() == 0) {
                // 第一个驼峰片段，全部字母都小写
                result.append(camel.toLowerCase());
            } else {
                // 其他的驼峰片段，首字母大写
                result.append(camel.substring(0, 1).toUpperCase());
                result.append(camel.substring(1).toLowerCase());
            }
        }
        return result.toString();
    }

    /**
     * 将下划线大写方式命名的字符串转换为驼峰式。
     * 如果转换前的下划线大写方式命名的字符串为空，则返回空字符串。</br>
     * 例如：hello_world,test_id->helloWorld,testId
     *
     * @param names 转换前的下划线大写方式命名的字符串
     * @return 转换后的驼峰式命名的字符串
     */
    public static String camelNames(String names) {
        if (isEmpty(names)) {
            return null;
        }
        StringBuilder sf = new StringBuilder();
        String[] fs = names.split(CommonConstant.COMMA);
        for (String field : fs) {
            field = camelName(field);
            sf.append(field).append(CommonConstant.COMMA);
        }
        String result = sf.toString();
        return result.substring(0, result.length() - 1);
    }

    /**
     * 将下划线大写方式命名的字符串转换为驼峰式。(首字母写)
     * 如果转换前的下划线大写方式命名的字符串为空，则返回空字符串。</br>
     * 例如：hello_world->HelloWorld
     *
     * @param name 转换前的下划线大写方式命名的字符串
     * @return 转换后的驼峰式命名的字符串
     */
    public static String camelNameCapFirst(String name) {
        StringBuilder result = new StringBuilder();
        // 快速检查
        if (name == null || name.isEmpty()) {
            // 没必要转换
            return CommonConstant.EMPTY;
        } else if (!name.contains(CommonConstant.UNDERSCORE)) {
            // 不含下划线，仅将首字母小写
            return name.substring(0, 1).toUpperCase() + name.substring(1).toLowerCase();
        }
        // 用下划线将原始字符串分割
        String[] camels = name.split(CommonConstant.UNDERSCORE);
        for (String camel : camels) {
            // 跳过原始字符串中开头、结尾的下换线或双重下划线
            if (camel.isEmpty()) {
                continue;
            }
            // 其他的驼峰片段，首字母大写
            result.append(camel.substring(0, 1).toUpperCase());
            result.append(camel.substring(1).toLowerCase());
        }
        return result.toString();
    }

    /**
     * 将驼峰命名转化成下划线
     *
     * @param para
     * @return
     */
    public static String camelToUnderline(String para) {
        if (para.length() < 3) {
            return para.toLowerCase();
        }
        StringBuilder sb = new StringBuilder(para);
//        定位
        int temp = 0;
//        从第三个字符开始 避免命名不规范
        for (int i = 2; i < para.length(); i++) {
            if (Character.isUpperCase(para.charAt(i))) {
                sb.insert(i + temp, CommonConstant.UNDERSCORE);
                temp += 1;
            }
        }
        return sb.toString().toLowerCase();
    }

    /**
     * 获取随机数生成器对象<br>
     * ThreadLocalRandom是JDK 7之后提供并发产生随机数，能够解决多个线程发生的竞争争夺。
     *
     * @return {@link ThreadLocalRandom}
     * @since 3.1.2
     */
    public static ThreadLocalRandom getRandom() {
        return ThreadLocalRandom.current();
    }

    /**
     * 随机数
     *
     * @param place 定义随机数的位数
     */
    public static String randomGen(int place) {
        StringBuilder sb = new StringBuilder();
        Random rd = new Random();
        for (int i = 0; i < place; i++) {
            sb.append(BASE_CHAR_NUMBER.charAt(rd.nextInt(BASE_CHAR_NUMBER.length())));
        }
        return sb.toString();
    }

    /**
     * 获得一个随机的字符串
     *
     * @param baseString 随机字符选取的样本
     * @param length     字符串的长度
     * @return 随机字符串
     */
    public static String randomString(String baseString, int length) {
        final StringBuilder sb = new StringBuilder();

        if (length < 1) {
            length = 1;
        }
        int baseLength = baseString.length();
        for (int i = 0; i < length; i++) {
            int number = getRandom().nextInt(baseLength);
            sb.append(baseString.charAt(number));
        }
        return sb.toString();
    }

    /**
     * 获得一个只包含数字的字符串
     *
     * @param length 字符串的长度
     * @return 随机字符串
     */
    public static String randomNumbers(int length) {
        return randomString(BASE_NUMBER, length);
    }

    /**
     * 获取类的所有属性，包括父类
     *
     * @param object
     * @return
     */
    public static Field[] getAllFields(Object object) {
        Class<?> clazz = object.getClass();
        List<Field> fieldList = new ArrayList<>();
        while (clazz != null) {
            fieldList.addAll(new ArrayList<>(Arrays.asList(clazz.getDeclaredFields())));
            clazz = clazz.getSuperclass();
        }
        Field[] fields = new Field[fieldList.size()];
        fieldList.toArray(fields);
        return fields;
    }

    /**
     * 将map的key全部转成小写
     *
     * @param list
     * @return
     */
    public static List<Map<String, Object>> toLowerCasePageList(List<Map<String, Object>> list) {
        List<Map<String, Object>> select = new ArrayList<>();
        for (Map<String, Object> row : list) {
            Map<String, Object> resultMap = new HashMap<>(16);
            Set<String> keySet = row.keySet();
            for (String key : keySet) {
                String newKey = key.toLowerCase();
                resultMap.put(newKey, row.get(key));
            }
            select.add(resultMap);
        }
        return select;
    }

    /**
     * 判断是不是数字,不是数字的话加上''
     *
     * @param text - 变量
     * @return null -
     * @author XinLau
     * @creed The only constant is change ! ! !
     * @since 2020/10/4 9:44
     */
    public static String judgeIsNumber(String text) {
        if (ConvertUtils.isNotEmpty(text)) {
            try {
                Double.valueOf(text);
            } catch (Exception e) {
                // 字段默认值，循环加了很多单引号
                // if (!(text.startsWith("\'") && text.endsWith("\'"))) {
                if (!(text.startsWith(CommonConstant.SINGLE_QUOTE) && text.endsWith(CommonConstant.SINGLE_QUOTE))) {
                    text = CommonConstant.SINGLE_QUOTE + text + CommonConstant.SINGLE_QUOTE;
                }
            }
        }
        return text;
    }

    /**
     * 比较两个对象内容是否相等
     *
     * @param oldValue - 旧值
     * @param newValue - 新值
     * @return null -
     * @author XinLau
     * @creed The only constant is change ! ! !
     * @since 2020/10/6 11:57
     */
    public static Boolean compareValue(Object oldValue, Object newValue) {
        if (isEmpty(oldValue) && isEmpty(newValue)) {
            return Boolean.TRUE;
        } else {
            if (isNotEmpty(oldValue) && oldValue.equals(newValue)) {
                return Boolean.TRUE;
            }
        }
        return Boolean.FALSE;
    }

}
